package com.cupdata.marathon.common;

import com.cupdata.marathon.bean.Result;
import com.cupdata.marathon.bean.User;
import com.cupdata.marathon.definition.CouponTp;
import com.cupdata.marathon.definition.DownRslt;
import com.cupdata.marathon.param.download.DownloadRequest;
import com.cupdata.marathon.utils.GsonUtils;
import org.redisson.api.RScript;
import org.redisson.api.RedissonClient;
import org.redisson.client.codec.StringCodec;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.stereotype.Service;

import javax.annotation.Resource;
import java.util.ArrayList;
import java.util.List;

import static com.cupdata.marathon.config.ConfigConstans.COUPON_ONE_REMAINS;
import static com.cupdata.marathon.config.ConfigConstans.COUPON_TWO_REMAINS;
import static org.redisson.api.RScript.Mode.READ_WRITE;

@Service
public class RedisService {
    private final Logger logger = LoggerFactory.getLogger(this.getClass());


    private static final String REDUCE_INVENTORY;

    private static final List<Object> keys_one = new ArrayList<>();
    private static final List<Object> keys_two = new ArrayList<>();

    private static boolean COUPON_ONE_DEPRECATE_LOCALREDIS = false;
    private static boolean COUPON_TWO_DEPRECATE_LOCALREDIS = false;

    static {
        StringBuilder sb = new StringBuilder();
        sb.append("local stock = tonumber(redis.call('get', KEYS[1]));");
        sb.append("local num = tonumber(ARGV[1]);");
        sb.append("if (stock > 0) then");
        sb.append("    redis.call('incrBy', KEYS[1], num);");
        sb.append("    return true;");
        sb.append("end;");
        sb.append("return false;");
        REDUCE_INVENTORY = sb.toString();
        keys_one.add(COUPON_ONE_REMAINS);
        keys_two.add(COUPON_TWO_REMAINS);
    }

    @Resource(name = "redissonCluster")
    RedissonClient redissonLocalClient;

    @Qualifier(value = "redissonSentinel")
    @Autowired
    RedissonClient redissonRemoteClient;

    @Autowired
    StringRedisTemplate stringRedisTemplate;

    @Autowired
    AuthCheckService authCheckService;


    //本地总数增减操作
    public Boolean localAtomicIncr(String couponTp, int size){
        return getaBoolean(couponTp, size, redissonLocalClient);

    }

    //远端总数增减操作
    public Boolean remoteAtomicIncr(String couponTp, int size){
        return getaBoolean(couponTp, size, redissonRemoteClient);

    }

    private Boolean getaBoolean(String couponTp, int size, RedissonClient redissonClient) {
        Object[] values=new Object[]{size};
        if (couponTp.startsWith("1")){
            return redissonClient.getScript(new StringCodec()).eval(
                    READ_WRITE, RedisService.REDUCE_INVENTORY, RScript.ReturnType.BOOLEAN, keys_one, values);
        }else{
            return redissonClient.getScript(new StringCodec()).eval(
                    READ_WRITE, RedisService.REDUCE_INVENTORY, RScript.ReturnType.BOOLEAN, keys_two, values);

        }
    }


    //弃用本地redis
    public void deprecateOneLocalRedis(){
        COUPON_ONE_DEPRECATE_LOCALREDIS = true;
    }

    public void deprecateTwoLocalRedis(){
        COUPON_TWO_DEPRECATE_LOCALREDIS = true;
    }

    //减少总优惠券
    public boolean deductCoupon(String couponTp) {

        if (couponTp.startsWith("1")){
            if (!COUPON_ONE_DEPRECATE_LOCALREDIS) {
                if (localAtomicIncr(couponTp,-1)) {
                    return true;
                }
            }
            return remoteAtomicIncr(couponTp,-1);
        }else{
            if (!COUPON_TWO_DEPRECATE_LOCALREDIS) {
                if (localAtomicIncr(couponTp,-1)) {
                    return true;
                }
            }
            return remoteAtomicIncr(couponTp,-1);
        }

    }






    //判断总优惠券是否用完
    public Boolean couponRemainsExist(String couponTp) {
        if (couponTp.startsWith("1")){
            return getcouponRemains(COUPON_ONE_REMAINS);

        }else{
            return getcouponRemains(COUPON_TWO_REMAINS);

        }

    }

    private Boolean getcouponRemains(String couponRemains) {
        if (COUPON_ONE_REMAINS.equals(couponRemains)){
            return getRemains(couponRemains, COUPON_ONE_DEPRECATE_LOCALREDIS);
        }else{
            return getRemains(couponRemains, COUPON_TWO_DEPRECATE_LOCALREDIS);
        }


    }

    private Boolean getRemains(String couponRemains, boolean couponDeprecateLocalredis) {
        long localRemains;
        if (!couponDeprecateLocalredis){
            localRemains =  redissonLocalClient.getAtomicLong(couponRemains).get();
            if(localRemains > 0){
                return true;
            }else{
                return redissonRemoteClient.getAtomicLong(couponRemains).get() > 0;
            }
        }else {
            return redissonRemoteClient.getAtomicLong(couponRemains).get() > 0;
        }
    }


    public void recoverCoupon(String userId, String couponType){
        //todo 恢复顺序？
        removeCoupon(userId, couponType);
        addCoupon(couponType);

    }
    //增加远端总优惠券
    public void addCoupon(String couponTp) {
        remoteAtomicIncr(couponTp,1);
    }
    //取消本地入账失败的优惠券
    public void removeCoupon(String userId, String couponType) {
        User user;
//        String keyLock = KEY_LOCK_VALUE + userId;
//        RLock redissonLock = redissonLocalClient.getLock(keyLock);
//        try {
//            redissonLock.tryLock(300, 300, TimeUnit.MILLISECONDS);
            String userInfo = stringRedisTemplate.opsForValue().get(userId);
            user = GsonUtils.gson.fromJson(userInfo, User.class);

            if (couponType.startsWith("1")) {
                user.setOneCount(user.getOneCount() - 1);

            } else {
                user.setTwoCount(user.getTwoCount() - 1);
            }
            user.setCouponWorth(user.getCouponWorth() - CouponTp.getValue(couponType));
            logger.info("该用户{}取消成功", userId);
            stringRedisTemplate.opsForValue().set(userId, GsonUtils.gson.toJson(user));

//        } catch (InterruptedException e) {
//            e.printStackTrace();
//        } finally {
//            redissonLock.unlock();
//        }
    }

    //下载优惠券校验
    public Result userCouponCheck(String userId, String couponTp, String membership) {
//        String keyLock = KEY_LOCK_VALUE + userId;
//        RLock redissonLock = redissonLocalClient.getLock(keyLock);
//        try {
//            redissonLock.tryLock(300, 300, TimeUnit.MILLISECONDS);

            String userInfoStr = stringRedisTemplate.opsForValue().get(userId);
            User user = GsonUtils.gson.fromJson(userInfoStr, User.class);

            Result x = authCheckService.userStatusCheck(userId, couponTp, membership, user);
            if (x != null) return x;

            // 校验通过，更新用户缓存数据
            if (couponTp.startsWith("1")) {
                user.setOneCount(user.getOneCount() + 1);
            } else {
                user.setTwoCount(user.getTwoCount() + 1);
            }
            user.setCouponWorth(user.getCouponWorth() + CouponTp.getValue(couponTp));
            stringRedisTemplate.opsForValue().set(userId, GsonUtils.gson.toJson(user));
            logger.info("该用户{}领取成功", userId);
            return new Result(DownRslt.R000000.getMsg(), DownRslt.R000000.getCode());
//
//        } catch (InterruptedException e) {
//            e.printStackTrace();
//            return null;
//        } finally {
//            redissonLock.unlock();
//        }

    }


    public void restoreCoupon(DownloadRequest request){
        logger.info("入账失败或非法 Restoring coupon");
        recoverCoupon(request.getUserId(), request.getCouponTp());
    }

}
